package com.ilia.util.set;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;


public class BitPlaneSet<T> implements Set<T>, Serializable{

	private static final long serialVersionUID = 1L;
	int[] data;
	int bitMask;
	int max_entry;
	int max_entry_mask;
	int max_entry_bit;
	int size;
	
	final static int WORD_BIT = 5;
	
	public BitPlaneSet() {
		this(28);
	}
	public BitPlaneSet(int max_entry_bit) {
	  this.max_entry_bit = max_entry_bit;
		this.max_entry = 1<<max_entry_bit;
		this.max_entry_mask = this.max_entry - 1;
		
		size = (max_entry>>WORD_BIT) + 1;
    int bit = 1;
    bitMask=1;
    int i = 0;
    while(i<WORD_BIT) {
      bit = 1 << i;
      bitMask = bitMask|bit; 
      i++;
    }
		clear();
	}
	
	public void add_internal(int number) {
		int i = number & max_entry_mask;
		int widx = i>>WORD_BIT;
		int bidx = i&bitMask;
		
		int word = data[widx];
		int value = 1<<bidx;
		word = word | value;
		data[widx] = word;
	}
	
	public boolean contains_internal(int number) {
		int i = number & max_entry_mask;
		
		int widx = i>>WORD_BIT;
		int bidx = i&bitMask;
		
		int word = data[widx];
		int value = 1<<bidx;
		
		boolean result = (word&value)!=0;
		return result;
	}
	
	
	public static void main(String[] argv) {
		BitPlaneSet<Integer> set = new BitPlaneSet<Integer>();
		set.add(1);
		assertTrue(set.size()==1);
		set.add(1023);
		set.add(1024);
		assertTrue(set.size()==3);
		set.add(32767);
		set.add(32768);
		assertTrue(set.size()==5);
		
		set.add(10);
		set.add(99);
		assertTrue(set.size()==7);
		set.add(100);
		set.add(101);
		assertTrue(set.size()==9);
		set.add(310);
		set.add(2087464132);
		set.add(-1320225355);
		
		assertTrue(set.contains(1));
		assertTrue(set.contains(1023));
		assertTrue(set.contains(1024));
		assertTrue(set.contains(32767));
		assertTrue(set.contains(32768));
		assertTrue(set.contains(10));
		assertTrue(set.contains(2087464132));
		assertTrue(set.contains(-1320225355));
		
		assertFalse(set.contains(2));
		assertFalse(set.contains(3));
		assertFalse(set.contains(4));
		assertFalse(set.contains(2087464131));
		assertFalse(set.contains(-1320225354));
		assertTrue(set.size()==12);

	}
	
	private static void assertTrue(boolean bool) {
		if (!bool) throw new RuntimeException("bool must be true: " +bool);
	}
	private static void assertFalse(boolean bool) {
		if (bool) throw new RuntimeException("bool must be false: " +bool);
	}
	

	
	public boolean add(T object) {
		Integer number = (Integer)object;
		int i = number & max_entry_mask;
		int widx = i>>WORD_BIT;
		int bidx = i&bitMask;
		
		int word = data[widx];
		int value = 1<<bidx;
		word = word | value;
		data[widx] = word;
		return true;
	}

	public boolean addAll(Collection c) {
		throw new RuntimeException();
	}

	public void clear() {
    data = new int[size];
	}

	@Override
	public boolean contains(Object object) {
		Integer number = (Integer)object;
		
		int i = number & max_entry_mask;
		
		int widx = i>>WORD_BIT;
		int bidx = i&bitMask;
		
		int word = data[widx];
		int value = 1<<bidx;
		
		boolean result = (word&value)!=0;
		return result;
		
	}

	public boolean containsAll(Collection c) {
		throw new RuntimeException();
	}

	public boolean isEmpty() {
		throw new RuntimeException();
	}

	@Override
	public Iterator iterator() {
		throw new RuntimeException();
	}

	@Override
	public boolean remove(Object o) {
		throw new RuntimeException();
	}

	@Override
	public boolean removeAll(Collection c) {
		throw new RuntimeException();
	}

	@Override
	public boolean retainAll(Collection c) {
		throw new RuntimeException();
	}

	@Override
	public int size() {
		int cnt=0;
		for (int widx = 0; widx < data.length; widx++) {
			int word = data[widx];
			for (int bidx = 0; bidx <= bitMask; bidx++) {
				int value = 1<<bidx;
				boolean result = (word&value)!=0;
				if (result) cnt++;
			}
		}
		return cnt;
	}

	@Override
	public Object[] toArray() {
		throw new RuntimeException();
	}

	@Override
	public Object[] toArray(Object[] a) {
		throw new RuntimeException();
	}

}
